package pers.whj.rpc.transport;

import pers.whj.rpc.common.Config;
import pers.whj.rpc.protocol.Address;
import pers.whj.rpc.server.ServerUtils;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * 实现服务端通信接口，采用TCP通信
 */
public class TCPServer implements TransportServer {
    private ServerSocket serverSocket;
    private RequestHandler handler;
    private boolean endFlag = true;
    private final Map<String, TCPThread> threads = new HashMap<>();

    @Override
    public void init(int port, RequestHandler handler) throws Exception {
        this.handler = handler;
        serverSocket = new ServerSocket(port);
        if (Config.DEBUG) {
            Address address = new Address(InetAddress.getLocalHost().getHostAddress(), port);
            System.out.println("tcp服务端初始化，地址：" + address);
        }
    }

    @Override
    public void start() throws Exception {
        endFlag = false;
        if (Config.DEBUG) {
            System.out.println("tcp服务端开启，等待连接...");
        }
        do {
            Socket client = serverSocket.accept();
            SocketThread thread = new SocketThread(client);
            thread.start();
            synchronized (threads) {
                threads.put(thread.getName(), thread);
            }
        } while (!endFlag);
    }

    @Override
    public void stop() {
        if (serverSocket != null) {
            if (Config.DEBUG) {
                System.out.println("tcp服务端关闭...");
            }
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public TCPThread getThread(String name) {
        synchronized (threads) {
            return threads.get(name);
        }
    }

    public void setEndFlag(boolean endFlag) {
        this.endFlag = endFlag;
    }

    public class SocketThread extends TCPThread {
        public SocketThread(Socket socket) {
            super(socket);
        }

        @Override
        public void run() {
            try {
                DataInputStream input = new DataInputStream(socket.getInputStream());
                DataOutputStream output = new DataOutputStream(socket.getOutputStream());
                socket.setSoTimeout(Config.TIME_OUT_NO_REQ);
                if (Config.DEBUG) {
                    Address address = new Address(socket.getInetAddress().getHostAddress(), socket.getPort());
                    System.out.println("客户端接入，地址：" + address);
                }
                boolean flag = true;
                do {
                    if (Config.DEBUG) {
                        System.out.println("等待读取数据...");
                    }
                    int len = input.readInt();
                    if (len <= Config.DATA_LENGTH_LIMIT_TO_END) {
                        flag = false;
                        if (Config.DEBUG) {
                            System.out.println("正断开连接...");
                        }
                    } else {
                        //读取请求数据
                        byte[] data = TransportUtils.readData(input, len);
                        //获取回应
                        if (handler != null) {
                            data = handler.onRequest(data);
                        } else {
                            data = ServerUtils.getErrorResponse("未知错误！");
                        }
                        //判断，如果过滤则不回应
                        if (data != null) {
                            output.writeInt(data.length);
                            output.write(data, 0, data.length);
                            output.flush();
                        }
                    }
                } while (flag);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (Config.DEBUG) {
                    System.out.println("已断开连接");
                }
                synchronized (threads) {
                    threads.remove(Thread.currentThread().getName());
                }
            }
        }
    }
}
